package net.omnivention.proxy;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.List;

import org.apache.log4j.Logger;

public class ProxyConnector implements Runnable {
	private static Logger logger = Logger.getLogger(ProxyConnector.class);

	private Socket clientSocket;

	private Socket hostSocket;

	private HttpRequest request;

	private List<Interceptor> interceptors;

	public ProxyConnector(Socket clientSocket, List<Interceptor> interceptors) {
		this.clientSocket = clientSocket;
		this.interceptors = interceptors;
		try {
			clientSocket.setSoTimeout(4000);
		} catch (SocketException e) {
			logger.warn(e, e);
		}
	}

	public void run() {
		try {
			request = HttpUtility.parseRequest(new BufferedInputStream(clientSocket.getInputStream()));

			String method = request.getMethod();
			String host = request.getHost();
			int port = request.getPort();
			if (HttpRequest.CONNECT.equals(method)) {
				sendConnectionEstablishToClient();
				clientSocket = IOUtility.createSSLWrapper(clientSocket);
				request = HttpUtility.parseRequest(new BufferedInputStream(clientSocket.getInputStream()));
				hostSocket = IOUtility.createSSLSocket(host, port);
			} else {
				hostSocket = IOUtility.createSocket(host, port);
			}

			request.setVersion("1.0");
			for (Interceptor i : interceptors) {
				i.intercept(request);
			}
			logger.info("\n-->\n" + request);
			
			//send request to host			
			BufferedOutputStream hostOut = new BufferedOutputStream(hostSocket.getOutputStream());
			hostOut.write(request.getAsByteArray());
			hostOut.write(HttpUtility.NEWLINE.getBytes());
			hostOut.flush();

			BufferedInputStream hostIn = new BufferedInputStream(hostSocket.getInputStream());
			HttpResponse response = HttpUtility.parseResponse(hostIn);

			for (Interceptor i : interceptors) {
				i.intercept(response);
			}
			logger.info("\n<--\n" + response);
			//send response to client
			BufferedOutputStream clientOut = new BufferedOutputStream(clientSocket.getOutputStream());
			clientOut.write(response.getAsByteArray());
			clientOut.flush();

		} catch (SocketTimeoutException e) {
			logger.debug(e, e);
		} catch (IOException e) {
			logger.error(e, e);
		} finally {
			try {
				logger.debug("closing " + hostSocket);
				hostSocket.close();
				logger.debug("closing " + clientSocket);
				clientSocket.close();
			} catch (IOException ex) {
			}
		}
	}

	/**
	 * According to specs at http://www.web-cache.com/Writings/Internet-Drafts/draft-luotonen-web-proxy-tunneling-01.txt Client
	 * will wait until proxy sends HTTP/1.1 200 Connection established\r\n
	 * 
	 * @param clientOutpuStream
	 * @throws IOException
	 */
	private void sendConnectionEstablishToClient() throws IOException {
		BufferedOutputStream clientOut = new BufferedOutputStream(clientSocket.getOutputStream());
		String r = "HTTP/1.0 200 Connection established\r\nProxy-agent: OmniProxy\r\n\r\n";
		clientOut.write(r.getBytes());
		clientOut.flush();
		logger.info("\n<--\n" + r);
	}
}
